﻿Imports System.Collections.ObjectModel
Imports System.Collections.Specialized

''' <summary>
''' テーブル属性
''' </summary>
''' <remarks></remarks>
<AttributeUsage(AttributeTargets.Class)>
Public Class TableAttribute
    Inherits Attribute

    Private Shared _onTableNameChanging As Func(Of String, String)
    ''' <summary>
    ''' テーブル名変更イベント
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' #101 生成されるSQL文にエスケープ文字を追加する
    ''' </remarks>
    Public Shared Property OnTableNameChanging As Func(Of String, String)
        Get
            If _onTableNameChanging Is Nothing Then
                _onTableNameChanging = Function(s) s
            End If
            Return _onTableNameChanging
        End Get
        Set(value As Func(Of String, String))
            _onTableNameChanging = value
        End Set
    End Property

    Public Sub New()
    End Sub

    Public Sub New(ByVal TableName As String)
        Me.TableName = TableName
    End Sub

#Region "BasicInfo"
    ''' <summary>
    ''' モデルタイプ
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ModelType As Type

    ''' <summary>
    ''' モデルタイプ略称
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' #79　基底クラスにマッピングされているテーブルを読み込んだときに継承を再現する
    ''' </remarks>
    Public Property ModelTypeAbbreviation As String

    Private _tableNamespace As String
    ''' <summary>
    ''' テーブル名前空間
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>ユーザー名、スキーマ名を指定します。</remarks>
    Public Property TableNamespace As String
        Get
            Return _tableNamespace
        End Get
        Set(value As String)
            '#101 生成されるSQL文にエスケープ文字を追加する
            _plainTableNamespace = value
            If String.IsNullOrEmpty(value) Then
                '空文字は補正しない
                _tableNamespace = value
            Else
                _tableNamespace = TableAttribute.OnTableNameChanging.Invoke(value)
            End If
        End Set
    End Property

    Private _plainTableNamespace As String
    ''' <summary>
    ''' テーブル名前空間（未エスケープ処理）
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' #101 生成されるSQL文にエスケープ文字を追加する
    ''' </remarks>
    Public ReadOnly Property PlainTableNamespace As String
        Get
            Return _plainTableNamespace
        End Get
    End Property

    Private _tableName As String
    ''' <summary>
    ''' テーブル名
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property TableName As String
        Get
            Return _tableName
        End Get
        Set(value As String)
            '#101 生成されるSQL文にエスケープ文字を追加する
            _palinTableName = value
            _tableName = TableAttribute.OnTableNameChanging.Invoke(value)
        End Set
    End Property

    Private _palinTableName As String
    ''' <summary>
    ''' テーブル名（未エスケープ処理）
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' #101 生成されるSQL文にエスケープ文字を追加する
    ''' </remarks>
    Public ReadOnly Property PlainTableName As String
        Get
            Return _palinTableName
        End Get
    End Property

    Private _loadTableName As String
    ''' <summary>
    ''' 読み込みテーブル名
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' 読み込み時のみ、テーブルを差し替えたい場合に指定します。
    ''' 省略した場合は、テーブル名が使用されます。
    ''' </remarks>
    Public Property LoadTableName As String
        Get
            Return If(String.IsNullOrEmpty(_loadTableName), Me.TableName, _loadTableName)
        End Get
        Set(value As String)
            _loadTableName = value
        End Set
    End Property

    ''' <summary>
    ''' テーブル正式名
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property TableFullName As String
        Get
            Return If(String.IsNullOrEmpty(Me.TableNamespace), Me.TableName, String.Format("{0}.{1}", Me.TableNamespace, Me.TableName))
        End Get
    End Property

    ''' <summary>
    ''' 読み込みテーブル正式名
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property LoadTableFullName As String
        Get
            Return If(String.IsNullOrEmpty(Me.TableNamespace), Me.LoadTableName, String.Format("{0}", Me.LoadTableName))
        End Get
    End Property

#End Region

#Region "Column"
    Private _columns As ObservableCollection(Of ColumnAttribute)
    ''' <summary>
    ''' カラムリスト
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    Public ReadOnly Property Columns() As ObservableCollection(Of ColumnAttribute)
        Get
            If _columns Is Nothing Then
                _columns = New ObservableCollection(Of ColumnAttribute)
                AddHandler _columns.CollectionChanged, AddressOf OnColumnsChanged
            End If
            Return _columns
        End Get
    End Property

    ''' <summary>
    ''' カラムリスト更新時
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub OnColumnsChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
        If e.NewItems IsNot Nothing AndAlso e.NewItems.Count <> 0 Then
            '追加した場合
            For Each item As ColumnAttribute In e.NewItems
                item.Table = Me
            Next
        End If
    End Sub
#End Region

#Region "Relation"

#Region "SuperRelation"
    ''' <summary>
    ''' 基底テーブルリレーションリスト
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property SuperRelations As New List(Of IParentRelation)

    ''' <summary>
    ''' 基底クラスへのカスケードができるかどうかを示します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property CanBaseCascade As Boolean = True

    ''' <summary>
    ''' 自身のクラスがルートであるかどうかを示します
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property IsRootBase() As Boolean
        Get
            '基底カスケードできない場合は、自身がルート
            If Me.CanBaseCascade = False Then Return True
            '#73　基底クラスがNothing型の場合は、自身がルート
            If Me.ModelType.BaseType Is Nothing Then Return True
            '基底クラスがObject型の場合は、自身がルート
            If Me.ModelType.BaseType.Equals(GetType(Object)) Then Return True
            '基底クラスが継承必須の場合、自身がルート
            If Me.ModelType.BaseType.IsAbstract Then Return True

            'それ以外はルートではない。基底クラスにカスケードする必要がある。
            Return False
        End Get
    End Property
#End Region

#Region "ChildRelation"
    Private _childRelations As ObservableCollection(Of ChildRelation)
    ''' <summary>
    ''' 子リレーションリスト
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    Public ReadOnly Property ChildRelations() As ObservableCollection(Of ChildRelation)
        Get
            If _childRelations Is Nothing Then _childRelations = New ObservableCollection(Of ChildRelation)
            AddHandler _childRelations.CollectionChanged, AddressOf OnChildRelationsChanged
            Return _childRelations
        End Get
    End Property

    ''' <summary>
    ''' 子リレーションリスト更新時
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub OnChildRelationsChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
        If e.NewItems IsNot Nothing AndAlso e.NewItems.Count <> 0 Then
            '追加した場合
            For Each item As ChildRelation In e.NewItems
                item.Table = Me
            Next
        End If
    End Sub
#End Region

#End Region

    Public Overrides Function ToString() As String
        Dim modelName As String = If(Me.ModelType Is Nothing, "unknown", Me.ModelType.Name)
        Return String.Format("{0} ({1})", Me.TableFullName, modelName)
    End Function

End Class
